#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import re
import json
import aiohttp
from urllib.parse import urlparse
from .config import Config
from .logger import Logger
from .models import VideoInfo, VideoPart, VideoQuality, Subtitle
import asyncio

class Parser:
    def __init__(self, url, config: Config):
        self.url = url
        self.config = config
        self.logger = Logger()
        self.bvid = None
        self.aid = None
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
            'Referer': 'https://www.bilibili.com',
            'Origin': 'https://www.bilibili.com',
            'Accept': '*/*',
            'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
            'Accept-Encoding': 'gzip, deflate, br',
            'Connection': 'keep-alive',
            'Sec-Fetch-Dest': 'empty',
            'Sec-Fetch-Mode': 'cors',
            'Sec-Fetch-Site': 'same-site'
        }
        # 添加cookie
        cookies = self.config.get_cookies()
        if cookies:
            self.headers['Cookie'] = cookies
    
    async def parse(self):
        """解析视频信息"""
        self._parse_url()
        return await self._get_video_info()
    
    def _parse_url(self):
        """解析URL获取视频ID"""
        # 处理BV号
        bv_match = re.search(r'BV\w+', self.url)
        if bv_match:
            self.bvid = bv_match.group()
            return
        
        # 处理av号
        av_match = re.search(r'av(\d+)', self.url, re.I)
        if av_match:
            self.aid = av_match.group(1)
            return
        
        # 处理短链接
        if 'b23.tv' in self.url:
            # TODO: 实现短链接解析
            pass
        
        raise ValueError('无法解析视频URL')
    
    async def _get_video_info(self):
        """获取视频信息"""
        if self.bvid:
            return await self._get_video_info_by_bvid(self.bvid)
        elif self.aid:
            return await self._get_video_info_by_aid()
        else:
            raise ValueError('无法获取视频信息')
    
    async def _get_video_info_by_bvid(self, bvid: str) -> VideoInfo:
        """通过BV号获取视频信息"""
        try:
            # 获取视频基本信息
            url = f"https://api.bilibili.com/x/web-interface/view?bvid={bvid}"
            self.logger.info(f"获取视频基本信息: {url}")
            async with aiohttp.ClientSession() as session:
                async with session.get(url, headers=self.headers) as response:
                    if response.status != 200:
                        error_msg = await response.text()
                        self.logger.error(f"获取视频基本信息失败: {response.status} - {error_msg}")
                        raise ValueError(f"获取视频基本信息失败: {response.status} - {error_msg}")
                    data = await response.json()
                    if data["code"] != 0:
                        self.logger.error(f"获取视频基本信息失败: {data['message']}")
                        raise ValueError(f"获取视频基本信息失败: {data['message']}")
                    video_data = data["data"]
                    self.logger.info(f"获取到视频基本信息: {video_data['title']}")
            
            # 获取视频分P信息
            url = f"https://api.bilibili.com/x/player/pagelist?bvid={bvid}"
            self.logger.info(f"获取视频分P信息: {url}")
            async with aiohttp.ClientSession() as session:
                async with session.get(url, headers=self.headers) as response:
                    if response.status != 200:
                        error_msg = await response.text()
                        self.logger.error(f"获取视频分P信息失败: {response.status} - {error_msg}")
                        raise ValueError(f"获取视频分P信息失败: {response.status} - {error_msg}")
                    data = await response.json()
                    if data["code"] != 0:
                        self.logger.error(f"获取视频分P信息失败: {data['message']}")
                        raise ValueError(f"获取视频分P信息失败: {data['message']}")
                    parts_data = data["data"]
                    self.logger.info(f"获取到视频分P信息: {len(parts_data)}个分P")
            
            # 创建视频信息对象
            video_info = VideoInfo(
                title=video_data["title"],
                author=video_data["owner"]["name"],
                cover=video_data["pic"],
                bvid=bvid,
                parts=[],
                qualities=[]
            )
            
            # 获取第一个分P的清晰度列表
            if parts_data:
                first_cid = parts_data[0]["cid"]
                self.logger.info(f"获取第一个分P的清晰度列表: CID={first_cid}")
                _, _, qualities = await self.get_video_play_info(bvid, first_cid)
                video_info.qualities = qualities
                self.logger.info(f"获取到清晰度列表: {[f'{q.height}P' for q in qualities]}")
            
            # 处理每个分P
            for part_data in parts_data:
                cid = part_data["cid"]
                self.logger.info(f"处理分P: {part_data['part']} (CID: {cid})")
                
                # 创建分P对象
                part = VideoPart(
                    cid=cid,
                    title=part_data["part"],
                    video_url="",  # 视频URL将在下载时设置
                    audio_url="",  # 音频URL将在下载时设置
                    danmaku_url=f"https://api.bilibili.com/x/v1/dm/list.so?oid={cid}",
                    subtitles=[],
                    qualities=video_info.qualities  # 使用相同的清晰度列表
                )
                
                video_info.parts.append(part)
            
            return video_info
            
        except Exception as e:
            self.logger.error(f"获取视频信息失败: {str(e)}")
            raise
    
    async def _get_video_info_by_aid(self):
        """通过av号获取视频信息"""
        async with aiohttp.ClientSession() as session:
            # 获取视频基本信息
            url = f'https://api.bilibili.com/x/web-interface/view?aid={self.aid}'
            async with session.get(url, headers=self.headers) as resp:
                if resp.status != 200:
                    raise ValueError('获取视频信息失败')
                data = await resp.json()
                if data['code'] != 0:
                    raise ValueError(data['message'])
                video_data = data['data']
                
                # 获取视频分P信息
                url = f'https://api.bilibili.com/x/player/pagelist?aid={self.aid}'
                async with session.get(url, headers=self.headers) as resp:
                    if resp.status != 200:
                        raise ValueError('获取分P信息失败')
                    data = await resp.json()
                    if data['code'] != 0:
                        raise ValueError(data['message'])
                    parts_data = data['data']
                
                # 获取视频播放地址
                url = f'https://api.bilibili.com/x/player/playurl?aid={self.aid}&cid={parts_data[0]["cid"]}&qn=80&fnval=16'
                async with session.get(url, headers=self.headers) as resp:
                    if resp.status != 200:
                        raise ValueError('获取播放地址失败')
                    data = await resp.json()
                    if data['code'] != 0:
                        raise ValueError(data['message'])
                    play_data = data['data']
                
                # 构建视频信息
                parts = []
                for part in parts_data:
                    parts.append(VideoPart(
                        cid=part['cid'],
                        title=part['part'],
                        video_url=play_data['durl'][0]['url'],
                        audio_url=play_data['dash']['audio'][0]['baseUrl'],
                        danmaku_url=f'https://api.bilibili.com/x/v1/dm/list.so?oid={part["cid"]}',
                        subtitles=[]
                    ))
                
                qualities = []
                for video in play_data['dash']['video']:
                    qualities.append(VideoQuality(
                        quality=str(video['bandwidth']),
                        height=video['height'],
                        url=video['baseUrl']
                    ))
                
                return VideoInfo(
                    title=video_data['title'],
                    cover=video_data['pic'],
                    parts=parts,
                    qualities=qualities
                )
    
    async def _get_image(self, url: str, max_retries: int = 3) -> bytes:
        """获取图片内容"""
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
            'Referer': 'https://www.bilibili.com',
            'Accept': 'image/webp,image/apng,image/*,*/*;q=0.8',
            'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
            'Accept-Encoding': 'gzip, deflate, br',
            'Connection': 'keep-alive',
            'Sec-Fetch-Dest': 'image',
            'Sec-Fetch-Mode': 'no-cors',
            'Sec-Fetch-Site': 'cross-site'
        }
        
        # 添加cookie
        cookies = self.config.get_cookies()
        if cookies:
            headers['Cookie'] = cookies
        
        for retry in range(max_retries):
            try:
                async with aiohttp.ClientSession() as session:
                    async with session.get(url, headers=headers, timeout=10) as response:
                        if response.status == 200:
                            return await response.read()
                        elif response.status == 403:
                            self.logger.error(f"获取图片被拒绝 (403): {url}")
                            # 如果是403错误，等待一段时间后重试
                            await asyncio.sleep(1)
                            continue
                        else:
                            self.logger.error(f"获取图片失败: {response.status} - {url}")
                            return None
            except asyncio.TimeoutError:
                self.logger.error(f"获取图片超时: {url}")
                if retry < max_retries - 1:
                    await asyncio.sleep(1)
                    continue
            except Exception as e:
                self.logger.error(f"获取图片异常: {str(e)} - {url}")
                if retry < max_retries - 1:
                    await asyncio.sleep(1)
                    continue
        
        return None

    async def get_video_play_info(self, bvid: str, cid: str) -> tuple[str, str, list[VideoQuality]]:
        """获取视频播放信息（视频URL、音频URL和清晰度列表）"""
        try:
            # 获取视频播放地址
            url = f"https://api.bilibili.com/x/player/playurl?bvid={bvid}&cid={cid}&qn=120&fnval=16&fourk=1"
            self.logger.info(f"获取视频播放地址: {url}")
            
            async with aiohttp.ClientSession() as session:
                async with session.get(url, headers=self.headers) as response:
                    if response.status != 200:
                        error_msg = await response.text()
                        self.logger.error(f"获取视频播放地址失败: {response.status} - {error_msg}")
                        raise ValueError(f"获取视频播放地址失败: {response.status} - {error_msg}")
                    data = await response.json()
                    if data["code"] != 0:
                        self.logger.error(f"获取视频播放地址失败: {data['message']}")
                        raise ValueError(f"获取视频播放地址失败: {data['message']}")
                    play_data = data["data"]
                    self.logger.info(f"获取到视频播放地址数据: {play_data.keys()}")
                
                # 获取音频播放地址
                if "dash" not in play_data:
                    self.logger.error("未找到dash数据")
                    raise ValueError("未找到dash数据")
                
                if not play_data["dash"]["audio"]:
                    self.logger.error("未找到音频流")
                    raise ValueError("未找到音频流")
                
                # 获取音频URL
                audio_url = play_data["dash"]["audio"][0]["baseUrl"]
                self.logger.info(f"获取到音频播放地址: {audio_url}")
                
                # 获取视频清晰度列表
                qualities = []
                if "dash" in play_data and "video" in play_data["dash"]:
                    for stream in play_data["dash"]["video"]:
                        quality = VideoQuality(
                            quality=str(stream["bandwidth"]),  # 确保quality是字符串
                            height=stream["height"],
                            width=stream["width"],
                            url=stream["baseUrl"]
                        )
                        qualities.append(quality)
                        self.logger.info(f"添加视频清晰度: {quality.height}P")
                    
                    # 按清晰度高度排序
                    qualities.sort(key=lambda q: q.height, reverse=True)
                    
                    # 设置默认视频URL为最高清晰度的URL
                    video_url = qualities[0].url if qualities else ""
                    self.logger.info(f"设置默认视频URL: {video_url}")
                    
                    return video_url, audio_url, qualities
                
                raise ValueError("未找到视频流")
            
        except Exception as e:
            self.logger.error(f"获取视频播放信息失败: {str(e)}")
            raise 